﻿using System;
using System.Linq;
using Granite.Modeling;
using Granite.Eventing;
using System.ComponentModel;
using Granite.Metadata;
using Toon.Models.Catalogs;
using System.Xml.Linq;
using Granite.DataAnnotations;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;

namespace Toon.Models.Characters
{
	public class Character : ChangeTrackingModelBase
	{
		readonly Catalog m_Catalog;
		readonly RuleSet m_Rules;

		public Character() :
			this(new StandardRules(new StandardCatalog()))
		{

		}

		public Character(RuleSet rules)
		{
			m_Rules = rules;
			m_Catalog = rules.Catalog;

			Properties.Set(new AttributeCollection((from a in m_Catalog.Attributes select new Attribute(a))), Granite.Modeling.Internals.PropertySetModes.SetAsOriginal, "Attributes");
			Attributes.ItemPropertyChanged += m_Attributes_ItemPropertyChanged;

			Properties.Set(new ShitckCollection(), Granite.Modeling.Internals.PropertySetModes.SetAsOriginal, "Shitcks");
			Shitcks.ItemPropertyChanged += Shitcks_ItemPropertyChanged;
			Shitcks.CollectionChanged += Shitcks_CollectionChanged;
		}

		private void Shitcks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			OnPropertyChanged("SkillPointsSpent");
		}

		private void Shitcks_ItemPropertyChanged(object sender, RelayedEventArgs<PropertyChangedEventArgs> e)
		{
			OnPropertyChanged("SkillPointsSpent");
		}

		void m_Attributes_ItemPropertyChanged(object sender, RelayedEventArgs<PropertyChangedEventArgs> e)
		{
			OnPropertyChanged("SkillPointsSpent");
			OnPropertyChanged("Speed");
		}

		public Catalog Catalog
		{
			get { return m_Catalog; }
		}

		public string Name
		{
			get { return Get<string>("", "Name"); }
			set { Set(value, "Name"); }
		}

		public string FileName
		{
			get { return Get<string>("FileName"); }
			set { Set(value, "FileName"); }
		}

		/// <summary>
		/// If the filename is not set this will return a suggested file name.
		/// </summary>
		/// <returns></returns>
		public string GetSuggestedFileName()
		{
			var result = FileName;

			if (!Granite.StringUtilities.IsNullOrWhiteSpace(result))
				return result;//return the cached file name

			result = Name;
			foreach (var c in Path.GetInvalidPathChars())
				result = result.Replace(c.ToString(), "");
			result = result.Replace(Path.DirectorySeparatorChar.ToString(), "");
			result = result.Replace(Path.DirectorySeparatorChar.ToString(), "");

			if (Granite.StringUtilities.IsNullOrWhiteSpace(result))
				return "";
			else
				return result + ".toon-character";
		}


		public RuleSet Rules
		{
			get { return m_Rules; }
		}

		public string Species
		{
			get { return Get<string>("", "Species"); }
			set { Set(value, "Species"); }
		}

		public string Occupation
		{
			get { return Get<string>("", "Occupation"); }
			set { Set(value, "Occupation"); }
		}

		public string NaturalEnemies
		{
			get { return Get<string>("", "NaturalEnemies"); }
			set { Set(value, "NaturalEnemies"); }
		}

		public string BeliefsAndGoals
		{
			get { return Get<string>("", "BeliefsAndGoals"); }
			set { Set(value, "BeliefsAndGoals"); }
		}

		public string Description
		{
			get { return Get<string>("", "Description"); }
			set { Set(value, "Description"); }
		}

		public PossessionCollection Possessions
		{
			get { return GetNew<PossessionCollection>("Possessions"); }
		}

		public AttributeCollection Attributes
		{
			get { return Get<AttributeCollection>("Attributes"); }
		}

		public ShitckCollection Shitcks
		{
			get { return Get<ShitckCollection>("Shitcks"); }
		}

		public int HitPoints
		{
			get { return Get<int>(0, "HitPoints"); }
			set { Set(value, "HitPoints"); }
		}

		public int MaxSkillPoints
		{
			get { return Rules.MaxSkillPoints; }
		}

		public int SkillPointsSpent
		{
			get
			{
				var points = Attributes.SelectMany(a => a.Skills).Sum(s => s.SkillPoints);
				points += Shitcks.SkillPointsSpent;
				return points;
			}
		}

		protected override void OnValidateObject(ValidationResultCollection results)
		{
			base.OnValidateObject(results);

			if (SkillPointsSpent > MaxSkillPoints)
				results.Add(new ValidationResult(
					String.Format("You have exceeded the maximum skill points by {0}.", (SkillPointsSpent - MaxSkillPoints))));
		}

		[CalculatedField("SkillPointsSpent")]
		public int UnspentSkillPoints
		{
			get { return MaxSkillPoints - SkillPointsSpent; }
		}

		public int Speed
		{
			get { return Attributes["Zip"].Value + 1; }
		}

		readonly Random m_Random = new Random();
		public Random Random
		{
			get { return m_Random; }
		}

		public XElement ToXml()
		{
			var result = new XElement("ToonCharacter",
							 new XAttribute("Ruleset", Rules.Name),
							 new XAttribute("Catalog", Catalog.Name),

							 new XAttribute("Name", Name),
							 new XAttribute("Species", Species),
							 new XAttribute("Occupation", Occupation),
							 new XAttribute("NaturalEnemies", NaturalEnemies),
							 new XAttribute("BeliefsAndGoals", BeliefsAndGoals),
							 new XAttribute("Description", Description),
							 new XAttribute("HitPoints", HitPoints),

							 new XElement("Attributes", Attributes.ToXml()),
							 new XElement("Shitcks", Shitcks.ToXml()),
							 new XElement("Possessions", Possessions.ToXml()));
			return result;
		}

		public Character(XElement source)
		{
			//TODO: Load other catalogs as needed
			m_Rules = new StandardRules(new StandardCatalog());
			m_Catalog = m_Rules.Catalog;

			Properties.Set(new AttributeCollection(from a in m_Catalog.Attributes select new Attribute(a)), Granite.Modeling.Internals.PropertySetModes.SetAsOriginal, "Attributes");
			Attributes.ItemPropertyChanged += m_Attributes_ItemPropertyChanged;

			Properties.Set(new ShitckCollection(), Granite.Modeling.Internals.PropertySetModes.SetAsOriginal, "Shitcks");
			Shitcks.ItemPropertyChanged += Shitcks_ItemPropertyChanged;
			Shitcks.CollectionChanged += Shitcks_CollectionChanged;

			Name = source.Attribute("Name").Value;
			Species = source.Attribute("Species").Value;
			Occupation = source.Attribute("Occupation").Value;
			NaturalEnemies = source.Attribute("NaturalEnemies").Value;
			BeliefsAndGoals = source.Attribute("BeliefsAndGoals").Value;
			Description = source.Attribute("Description").Value;
			HitPoints = int.Parse(source.Attribute("HitPoints").Value);

			Attributes.FromXml(source.Element("Attributes"));
			Shitcks.FromXml(source.Element("Shitcks"));
			Possessions.FromXml(source.Element("Possessions"));

			AcceptChanges();
		}

	}
}